package ntrees.hu.test;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.util.Log;

public class ImageLoader {
	public static final String TAG = "ImageLoader";
	public static final boolean LOGV = false;
	public static final int CACHE_COUNT = 48;
	
	private HashMap<String, SoftReference<Bitmap>> mCache = new HashMap<String, SoftReference<Bitmap>>();
	private ArrayList<LoadBitmapBin> mQueue = new ArrayList<LoadBitmapBin>();;
	private Handler mHandler;
	private boolean mStop;
	private LoadBitmapBin inProgressBin;
	private boolean threadStarted;
	private Context mContext;
	
	public interface LoadedCallback{
		void onBitmapLoaded(Bitmap loadedBitmap);
	}
	
	public class LoadBitmapBin{
		public String url;
		public LoadedCallback callback;
		
		
		/**
		 * @param i Index of the image, it's must be unique and URI is computed by it.
		 * @param cb
		 */
		public LoadBitmapBin(String urlStr, LoadedCallback cb){
		    url = urlStr;
			callback = cb;
		}
		
		public boolean equals(Object object){
			LoadBitmapBin bin = (LoadBitmapBin) object;
			
			if(!bin.url.equals(this.url)){
				return false;
			}else{
				return true;
			}
		}
		
		public int hashCode() {
            return url.hashCode();
        }
		
		
	}
	
	public ImageLoader(Context context, Handler handler){
		mContext = context;
		mHandler = handler;
	}
	
    public Bitmap getFromCache(String urlStr) {
        if (mCache.containsKey(urlStr)) {
            SoftReference<Bitmap> br = mCache.get(urlStr);
            Bitmap b = br.get();
            if (b == null) {
                if (LOGV)
                    Log.v(TAG, "cache is cleared by gc.");
                mCache.remove(urlStr);
            } else {
                return b;
            }
        }
        return null;
    }

	public Bitmap getBitmap(String urlStr, LoadedCallback callback, boolean needToFront){
	    Bitmap b = getFromCache(urlStr);
        if(b!=null){
            return b;
        }
	    if(!threadStarted){
			start();
		}
		synchronized(mQueue){
			if(mQueue != null){
				LoadBitmapBin loadBin = new LoadBitmapBin(urlStr, callback);
				if(inProgressBin == null || !inProgressBin.equals(loadBin)){
					boolean contains = mQueue.contains(loadBin);
					if(contains){
						if(needToFront){
							mQueue.remove(loadBin);
							mQueue.add(0, loadBin);
						}
					}else{
						if(needToFront){
							mQueue.add(0, loadBin);
						}else{
							mQueue.add(loadBin);
						}
					}
					mQueue.notifyAll();
				}
			}
		}
		return null;
	}
	
	public void clearLoadImage(){
		synchronized(mQueue){
			if(mQueue != null){
				mQueue.clear();
			}
		}
	}
	
	synchronized private void start(){
		if(threadStarted){
			return;
		}
		if(LOGV)
		Log.v(TAG,"thread start!!");
		threadStarted = true;
		mStop = false;
		Thread thread = new Thread(){
			
			public void run(){
				while(!mStop){
					LoadBitmapBin bin = null;
					synchronized(mQueue){
						if(mQueue.size() > 0){
							bin = mQueue.remove(0);
							inProgressBin = bin;
						}else{
							try {
								mQueue.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
					
					if(bin != null){
						String urlStr = bin.url;
						final LoadedCallback cb = bin.callback;
						if(LOGV){
							Log.v(TAG, "getBitmap url: "+urlStr);
						}
						Bitmap bitmap = null;
						try{
						    bitmap = getBitmap(urlStr);
						}catch(OutOfMemoryError e){
						    Log.e("no more memory", "call gc");
						    System.gc();
						    bitmap = getBitmap(urlStr);
						}
						
						final Bitmap b = bitmap;
						if(mHandler !=null){
							mHandler.post(new Runnable(){

								public void run() {
									cb.onBitmapLoaded(b);
								}
								
							});
						}else{
							cb.onBitmapLoaded(b);
						}
						SoftReference<Bitmap> br = new SoftReference<Bitmap>(b);
                        mCache.put(urlStr, br);
					}
				}
			}
			
		};
		thread.start();
		
	}
	
	/*protected*/public Bitmap getBitmap(String urlstr){

        Bitmap bitmap = null;
        try{
            URL url = new URL(urlstr);
            URLConnection conn = url.openConnection();
            conn.connect();
            InputStream in = conn.getInputStream();
            BufferedInputStream bis= new BufferedInputStream(in);
            
//            bitmap = ImageGetter.getInstance(null).decodeBitmapThumnail(bis);
            BitmapFactory.Options options=new BitmapFactory.Options(); 
            options.inJustDecodeBounds = false; 
            options.inDensity = 0;
//          options.inTempStorage = new byte[16*1024];
            options.inPreferredConfig = Bitmap.Config.RGB_565; 
            options.inSampleSize = 1; 
            bitmap = BitmapFactory.decodeStream(bis,null,options);
            //Utils.log("bitmap height:", ""+bitmap.getHeight());
            //Utils.log("bitmap width:", ""+bitmap.getWidth());
            //bitmap = getBgBitmap(bitmap);
            in.close();
            bis.close();
            //bitmap = BitmapFactory.decodeStream(bis);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        if(bitmap == null){
            Log.e("ImageLoader","ImageLoader occurs some errors when load bitmap.");
        }
        return bitmap;
	}
	
	//because default random bitmap is 200*150.
	private Bitmap getBgBitmap(Bitmap bitmap) {
        int srcWidth = bitmap.getWidth();
        int srcHeight = bitmap.getHeight();
        float a = srcHeight/ srcWidth ;
        Matrix matrix = new Matrix();
        //width bigger.
        if(a < 0.75){
            matrix.postScale( 200f/(float)srcWidth, 200f/(float)srcWidth);
            int y = 75 - (srcHeight * 200 / srcWidth)/2;
            matrix.postTranslate(0, y);
        }else{
            matrix.postScale( 150f/(float)srcHeight, 150f/(float)srcHeight);
            int x = 100 - (srcWidth * 150/srcHeight)/2;
            matrix.postTranslate(x, 0);
        }
        Bitmap b = null;
        try{
            b = Bitmap.createBitmap(200, 150, Config.RGB_565);
        }catch(OutOfMemoryError e){
            System.gc();
            b = Bitmap.createBitmap(200, 150, Config.RGB_565);
        }
        Canvas c = new Canvas(b);
        c.drawColor(Color.BLACK);
        c.drawBitmap(bitmap, matrix, new Paint());
        bitmap.recycle();
        bitmap = null;
        return b;
    }

    public void stopLoad(){
		mStop = true;
		synchronized(mQueue){
			mQueue.notifyAll();
		}
		threadStarted = false;
	}
	
	public void stopLoad(String[] urls){
		synchronized(mQueue){
			for(String u : urls){
				if(mQueue.contains(u)){
					mQueue.remove(u);
				}
			}
		}
	}
	
	//Use this method before u use the bitmap which cached in the loader. 
	//So u will avoid use the recycled bitmap.
	public void recycleAtMost(){
	    recycle(CACHE_COUNT);
	}
	
	public void recycle(int saveCount){
	    if(mCache.size() > saveCount) {
	        synchronized(mQueue) {
	            Set<String> set = mCache.keySet();
	            String[] cacheUrls = new String[set.size()]; 
                set.toArray(cacheUrls);
	            for(String strUrl : cacheUrls){
	                int size = mCache.size();
	                if(size > saveCount){
	                    SoftReference<Bitmap> softBmp= mCache.remove(strUrl);
	                    Bitmap bitmap = softBmp.get();
	                    if(bitmap != null){
	                        bitmap.recycle();
	                    }
	                }else {
	                    break;
	                }
	            }
	        }
	    }
	}
	
	public void recycleAll(){
	    recycle(0);
	}
	
	public void recycleUrls(String[] recycleUrls) {
	    if(recycleUrls == null) {
	        return;
	    }
	    synchronized(mQueue) {
            Set<String> set = mCache.keySet();
            String[] cacheUrls = new String[set.size()]; 
            set.toArray(cacheUrls);
            for(String strUrl : cacheUrls){
                for(String rcu : recycleUrls){
                    if(strUrl.equals(rcu)){
                        SoftReference<Bitmap> softBmp= mCache.remove(strUrl);
                        Bitmap bitmap = softBmp.get();
                        Log.v("remove url:",strUrl);
                        if(bitmap != null){
                            Log.v("recycle url:",strUrl);
                            bitmap.recycle();
                        }
                        break;
                    }
                }
            }
        }
	}
	
	public int getQueueCount() {
	    synchronized(mQueue){
	        return mQueue.size();
	    }
	}
	
	public void recycleExcept(int saveCount, String[] exceptUrls){
	    synchronized(mQueue){
	        if(exceptUrls == null) {
                return;
            }
	        if(saveCount < exceptUrls.length){
	            throw new RuntimeException("The save count be less than the except ones.");
	        }
	        //Utils.log("remove url:","expect:" + exceptUrls.length);
	        //Utils.log("remove url:","count in cache:" + mCache.size());
	        if(mCache.size() > saveCount) {
	            Set<String> set = mCache.keySet();
	            String[] cacheUrls = new String[set.size()]; 
	            set.toArray(cacheUrls);
	            for(String strUrl : cacheUrls) {
	                boolean contain = false;
	                for(String u : exceptUrls) {
	                    if(u.equals(strUrl)) {
	                        //Utils.log("remove url:","find contain:" + u);
	                        contain = true;
	                        break;
	                    }
	                }
	                if(mCache.size() <= saveCount) {
	                    return;
	                }
	                if(!contain){
	                    SoftReference<Bitmap> softBmp= mCache.remove(strUrl);
	                    //Utils.log("remove url:",strUrl);
	                    Bitmap bitmap = softBmp.get();
	                    if(bitmap != null){
	                        //Utils.log("recycle url:",strUrl);
	                        bitmap.recycle();
	                    }
	                }
	            }  
	        }
	    }
	}
}
